from django.shortcuts import render_to_response, redirect
from django.http import HttpResponse, HttpResponseForbidden, HttpResponseNotFound

import logging
import os.path
import urllib
import json

import auth
import client
import settings
import utils
import elements
from dav_exceptions import *

log = logging.getLogger('views')
log.setLevel(level=settings.LOG_LEVEL)
handler = logging.handlers.RotatingFileHandler(settings.LOG_FILE, maxBytes=settings.LOG_FILE_SIZE)
handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
log.addHandler(handler)

def login(request):
    if request.method == 'GET':
        return render_to_response('login.tpl')
    elif request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        if auth.authenticate(username, password):
            auth.login(request, username, password)
            return HttpResponse('/%s/' % username)
        else:
            msg = 'Login fail.'
            return HttpResponse(msg)

def logout(request):
    if request.method == 'POST':
        auth.logout(request)
        return HttpResponse('Logout success.')
    else:
        return HttpResponse('Logout failed.')

def register(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        try:
            multistatus = client.propfind(auth.gen_credentials('superadmin', ''),
                                          '/users/%s' % username,
                                          ['{DAV:}displayname'])
            fileList = _multistatus_to_fileList(multistatus)
            if len(fileList) == 1:
                return HttpResponse('Already exist.')
        except NotFound:
            if client.put(auth.gen_credentials('superadmin', ''),
                          '/users/%s' % username, ' '):
                return HttpResponse('Created.')
            else:
                return HttpResponse('Register error.')

def login_required(function, redirect_field_name=settings.LOGIN_URL):
    def decorator(func):
        def wrapper(request, *args, **kwargs):
            if auth.is_authenticated(request):
                return func(request, *args, **kwargs)
            else:
                return redirect(redirect_field_name)
        return wrapper

    return decorator(function)

@login_required
def dispatch(request):
    if request.method == 'GET':
        if not 'action' in request.GET:
            return get(request)
        elif request.GET['action'] == 'lsdir':
            return lsdir(request)
        elif request.GET['action'] == 'lsgroup':
            return lsgroup(request)
        elif request.GET['action'] == 'delete':
            return delete(request)
        elif request.GET['action'] == 'mkcol':
            return mkcol(request)
        elif request.GET['action'] == 'copy':
            return copy(request)
        elif request.GET['action'] == 'move':
            return move(request)
        elif request.GET['action'] == 'grant':
            return grant(request)
        elif request.GET['action'] == 'deny':
            return deny(request)
        elif request.GET['action'] == 'propertyupdate':
            return properupdate(request)
        elif request.GET['action'] == 'search':
            return search(request)
        elif request.GET['action'] == 'properties':
            return properties(request)
        elif request.GET['action'] == 'acl':
            return acl(request)
        elif request.GET['action'] == 'group-member-set':
            return group_member_set(request)
        elif request.GET['action'] == 'join-group':
            return join(request)
    if request.method == 'POST':
        if request.META['SCRIPT_NAME'] == u'/login.html':
            return login(request)
        if request.META['SCRIPT_NAME'] == u'/logout.html':
            return logout(request)
        elif request.META['SCRIPT_NAME'] == u'/register.html':
            return register(request)
        else:
            return post(request)

def get(request):
    msg = 'Get'
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    if path.endswith('/'):
        return render_to_response('index.tpl', locals())
    else:
        try:
            url = client.get(request.user, path)
        except NeedPrivileges:
            return HttpResponseForbidden()
        except NotFound:
            return HttpResponseNotFound()
        if url:
            return redirect(url)
        else:
            return HttpResponse('Error')

def lsdir(request):
    msg = 'List directory'
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    parent = utils.parent(path)
    try:
        multistatus = client.propfind(request.user, path, ['{DAV:}displayname'], depth=1)
        fileList = _multistatus_to_fileList(multistatus)
        return render_to_response('fileList.tpl', locals())
    except NotFound:
        pass
    except NeedPrivileges:
        return HttpResponse('403 Forbidden')

def lsgroup(request):
    msg = 'List groups'
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    parent = utils.parent(path)
    multistatus = client.principal_match(request.user, path, '{DAV:}owner', ['{DAV:}displayname'], depth=1)
    fileList = _multistatus_to_fileList(multistatus)
    return render_to_response('groupList.tpl', locals())
        
def post(request):
    msg = 'Post'
    path = request.GET['path'] + urllib.quote((request.GET['qqfile']).strip().encode('utf-8'))
    try:
        if client.put(request.user, path, request.raw_post_data):
            return HttpResponse('{success:true}')
    except NeedPrivileges:
        return HttpResponse('{error:\'403 Forbidden\'}')

def mkcol(request):
    msg = 'Mkcol'
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    log.debug('Make Collection: %s' % path)
    status = client.mkcol(request.user, path)
    log.debug('Finish making Collection: %s' % path)
    if status == 201:
        msg = 'Created'
    elif status == 405:
        msg = 'Already exists'
    elif status == 409:
        msg = 'Conflict'
    else:
        msg = status
    return HttpResponse(msg)

def delete(request):
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    try:
        if client.delete(request.user, path):
            return HttpResponse('Deleted')
    except NeedPrivileges:
        return HttpResponse('403 Forbidden')
    except NotFound:
        return HttpResponse('404 NotFound')

def copy(request):
    msg = 'Copy'
    return render_to_response('index.tpl', locals())

def move(request):
    if not 'dst' in request.GET:
        return HttpResponse('Error request.')
    else:
        src = urllib.quote(request.META['SCRIPT_NAME'].strip().encode('utf-8'))
        dst = urllib.quote(request.GET['dst'].strip().encode('utf-8'))
        status = client.move(request.user, src, dst)
        if status == 201:
            return HttpResponse('Moved.')
        else:
            return HttpResponse('Status :%s. Please try again.' % status)

def grant(request):
    if not 'privilege' in request.GET or not 'principal' in request.GET:
        return HttpResponse('Error request.')
    else:
        path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
        if 'add' in request.GET:
            try:
                if client.grant(request.user,
                                path,
                                request.GET['principal'],
                                request.GET['privilege'],
                                True):
                    return HttpResponse('Granted.')
            except NotFound:
                return HttpResponse('403 Not Found')
        elif 'remove' in request.GET:
            try:
                if client.grant(request.user,
                                path,
                                request.GET['principal'],
                                request.GET['privilege'],
                                False):
                    return HttpResponse('Granted.')
            except NotFound:
                return HttpResponse('403 Not Found')
        else:
            return HttpResponse('409 Conflict')

def properupdate(request):
    if not 'name' in request.GET or not 'value' in request.GET:
        return HttpResponse('Error request.')
    else:
        path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
        if 'set' in request.GET:
            responseList = client.propertyupdate(request.user, path, set_names=[request.GET['name']], set_values=[request.GET['value']])
        elif 'remove' in request.GET:
            responseList = client.propertyupdate(request.user, path, remove_names=[request.GET['name']], remove_values=[request.GET['value']])
        else:
            return HttpResponse('Error request.')
    return HttpResponse('Updated.')

def search(request):
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    if request.GET['type'] == 'file':
        kvdict = dict(request.GET)
        kvdict.pop('type')
        kvdict.pop('action')
        filters = [(k, v[0]) for k, v in kvdict.items()]
        multistatus = client.principal_property_search(request.user, path, filters, ['{DAV:}displayname'], depth='infinity')
        fileList = _multistatus_to_fileList(multistatus)
        log.debug(fileList)
        return render_to_response('fileSearchResult.tpl', locals())
    elif request.GET['type'] == 'user':
        multistatus = client.principal_property_search(request.user, '/users/', [('displayname', request.GET['term'])], ['{DAV:}displayname'], depth='1')
        userList = _multistatus_to_fileList(multistatus)
        result = []
        for iscol, href, props in userList:
            result.append({'label':props['displayname'].displayname, 'value':href})
        return HttpResponse(json.dumps(result))
    elif request.GET['type'] == 'group':
        multistatus = client.principal_property_search(request.user, '/groups/', [('displayname', request.GET['term'])], ['{DAV:}displayname'], depth='1')
        userList = _multistatus_to_fileList(multistatus)
        result = []
        for iscol, href, props in userList:
            result.append({'label':props['displayname'].displayname, 'value':href})
        return HttpResponse(json.dumps(result))
    elif request.GET['type'] == 'principal':
        multistatus = client.principal_property_search(request.user, '/groups/', [('displayname', request.GET['term'])], ['{DAV:}displayname'], depth='1')
        userList = _multistatus_to_fileList(multistatus)
        result = []
        for iscol, href, props in userList:
            result.append({'label':props['displayname'].displayname, 'value':href})
        multistatus = client.principal_property_search(request.user, '/users/', [('displayname', request.GET['term'])], ['{DAV:}displayname'], depth='1')
        userList = _multistatus_to_fileList(multistatus)
        for iscol, href, props in userList:
            result.append({'label':props['displayname'].displayname, 'value':href})
        return HttpResponse(json.dumps(result))

def properties(request):
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    multistatus = client.propfind(request.user,
                                  path,
                                  ['{DAV:}creationdate',
                                   '{DAV:}getlastmodified',
                                   '{DAV:}getcontentlength',
                                   '{DAV:}displayname',
                                   '{DAV:}owner',
                                   '{DAV:}group'],
                                  depth=0
                                  )
    if multistatus:
        iscol, href, props = _multistatus_to_fileList(multistatus)[0]
        return render_to_response('properties.tpl', locals())
    else:
        return HttpResponse('Error request %s' % request)

def acl(request):
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    multistatus = client.propfind(request.user, path, ['{DAV:}acl'], depth=0)
    iscol, href, props = _multistatus_to_fileList(multistatus)[0]
    return render_to_response('acl.tpl', locals())
    
def group_member_set(request):
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    multistatus = client.propfind(request.user,
                                  path,
                                  ['{DAV:}group-member-set'],
                                  depth=0
                                  )
    iscol, href, props = _multistatus_to_fileList(multistatus)[0]
    return render_to_response('group_member_set.tpl', locals())

def join(request):
    if not 'principal' in request.GET:
        return HttpResponse('Error request.')
    path = urllib.quote(request.META['SCRIPT_NAME'].encode('utf-8'))
    multistatus = client.propfind(request.user,
                                  path,
                                  ['{DAV:}group-member-set'],
                                  depth=0
                                  )
    gms = multistatus.responseList[0].propstatList[0].prop['{DAV:}group-member-set']
    if 'join' in request.GET:
        exists = False
        for href in gms.hrefList:
            if href.href == request.GET['principal']:
                exists = True
        if not exists:
            gms.hrefList.append(elements.DAVHref(href=request.GET['principal']))
    if 'exit' in request.GET:
        for href in gms.hrefList:
            if href.href == request.GET['principal']:
                gms.hrefList.remove(href)
                break
    responseList = client.propertyupdate(request.user, path, set_names=['{DAV:}group-member-set'], set_values=[gms.dumpXML()])
    return HttpResponse(responseList[0].propstatList[0].status.status)

def _multistatus_to_fileList(multistatus):
    '''
    Return:
        fileList: [iscol, href, props]
    '''
    fileList = []
    for response in multistatus.responseList:
        fileProps = {}
        for propstat in response.propstatList:
            for oneprop in propstat.prop.getchildren():
                fileProps[utils.property2variable(oneprop.getTag()[6:])] = oneprop
        fileList.append([response.hrefList[0].href.endswith('/'), response.hrefList[0].href, fileProps])
    return fileList
